 /*******************************************************************************
  * Copyright (c) 2004, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - Initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal.browser;

 import java.beans.PropertyChangeEvent ;
 import java.beans.PropertyChangeListener ;
 import java.io.File ;
 import java.util.ArrayList ;
 import java.util.List ;

 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.jface.action.IStatusLineManager;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.SWTError;
 import org.eclipse.swt.SWTException;
 import org.eclipse.swt.browser.Browser;
 import org.eclipse.swt.browser.CloseWindowListener;
 import org.eclipse.swt.browser.LocationEvent;
 import org.eclipse.swt.browser.LocationListener;
 import org.eclipse.swt.browser.OpenWindowListener;
 import org.eclipse.swt.browser.ProgressEvent;
 import org.eclipse.swt.browser.ProgressListener;
 import org.eclipse.swt.browser.StatusTextEvent;
 import org.eclipse.swt.browser.StatusTextListener;
 import org.eclipse.swt.browser.TitleEvent;
 import org.eclipse.swt.browser.TitleListener;
 import org.eclipse.swt.browser.VisibilityWindowListener;
 import org.eclipse.swt.browser.WindowEvent;
 import org.eclipse.swt.dnd.Clipboard;
 import org.eclipse.swt.events.MouseEvent;
 import org.eclipse.swt.events.MouseListener;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.layout.FillLayout;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Combo;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Event;
 import org.eclipse.swt.widgets.Listener;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.swt.widgets.ToolBar;
 import org.eclipse.swt.widgets.ToolItem;
 import org.eclipse.ui.PlatformUI;

 /**
  * A Web browser widget. It extends the Eclipse SWT Browser widget by adding an
  * optional toolbar complete with a URL combo box, history, back & forward, and
  * refresh buttons.
  * <p>
  * Use the style bits to choose which toolbars are available within the browser
  * composite. You can access the embedded SWT Browser directly using the
  * getBrowser() method.
  * </p>
  * <p>
  * Additional capabilities are available when used as the internal Web browser,
  * including status text and progress on the Eclipse window's status line, or
  * moving the toolbar capabilities up into the main toolbar.
  * </p>
  * <dl>
  * <dt><b>Styles:</b></dt>
  * <dd>LOCATION_BAR, BUTTON_BAR</dd>
  * <dt><b>Events:</b></dt>
  * <dd>None</dd>
  * </dl>
  *
  * @since 1.0
  */
 public class BrowserViewer extends Composite {
     /**
      * Style parameter (value 1) indicating that the URL and Go button will be
      * on the local toolbar.
      */
     public static final int LOCATION_BAR = 1 << 1;

     /**
      * Style parameter (value 2) indicating that the toolbar will be available
      * on the web browser. This style parameter cannot be used without the
      * LOCATION_BAR style.
      */
     public static final int BUTTON_BAR = 1 << 2;
      
      protected static final String PROPERTY_TITLE = "title"; //$NON-NLS-1$

     private static final int MAX_HISTORY = 50;

     public Clipboard clipboard;

     public Combo combo;

     protected boolean showToolbar;

     protected boolean showURLbar;

     protected ToolItem back;

     protected ToolItem forward;

     protected BusyIndicator busy;

     protected boolean loading;

     protected static java.util.List history;

     protected Browser browser;
     
     protected BrowserText text;

     protected boolean newWindow;

     protected IBrowserViewerContainer container;

     protected String title;

     protected int progressWorked = 0;
      
      protected List propertyListeners;

     /**
      * Under development - do not use
      */
     public static interface ILocationListener {
         public void locationChanged(String url);

         public void historyChanged(String [] history2);
     }

     public ILocationListener locationListener;

     /**
      * Under development - do not use
      */
     public static interface IBackNextListener {
         public void updateBackNextBusy();
     }

     public IBackNextListener backNextListener;

     /**
      * Creates a new Web browser given its parent and a style value describing
      * its behavior and appearance.
      * <p>
      * The style value is either one of the style constants defined in the class
      * header or class <code>SWT</code> which is applicable to instances of
      * this class, or must be built by <em>bitwise OR</em>'ing together (that
      * is, using the <code>int</code> "|" operator) two or more of those
      * <code>SWT</code> style constants. The class description lists the style
      * constants that are applicable to the class. Style bits are also inherited
      * from superclasses.
      * </p>
      *
      * @param parent
      * a composite control which will be the parent of the new
      * instance (cannot be null)
      * @param style
      * the style of control to construct
      */
     public BrowserViewer(Composite parent, int style) {
         super(parent, SWT.NONE);
           
         if ((style & LOCATION_BAR) != 0)
             showURLbar = true;

         if ((style & BUTTON_BAR) != 0)
             showToolbar = true;

         GridLayout layout = new GridLayout();
         layout.marginHeight = 0;
         layout.marginWidth = 0;
         layout.horizontalSpacing = 0;
         layout.verticalSpacing = 0;
         layout.numColumns = 1;
         setLayout(layout);
         setLayoutData(new GridData(GridData.FILL_BOTH));
         clipboard = new Clipboard(parent.getDisplay());
         
         if (showToolbar || showURLbar) {
             Composite toolbarComp = new Composite(this, SWT.NONE);
             toolbarComp.setLayout(new ToolbarLayout());
             toolbarComp.setLayoutData(new GridData(
                   GridData.VERTICAL_ALIGN_BEGINNING
                   | GridData.FILL_HORIZONTAL));

             if (showToolbar)
                 createToolbar(toolbarComp);
             
                 if (showURLbar)
                 createLocationBar(toolbarComp);

                 if (showToolbar | showURLbar) {
                     busy = new BusyIndicator(toolbarComp, SWT.NONE);
                     busy.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_END));
                     busy.addMouseListener(new MouseListener() {
                         public void mouseDoubleClick(MouseEvent e) {
                             // ignore
 }

                         public void mouseDown(MouseEvent e) {
                             setURL("http://www.eclipse.org"); //$NON-NLS-1$
 }

                         public void mouseUp(MouseEvent e) {
                             // ignore
 }
                     });
                 }
                 PlatformUI.getWorkbench().getHelpSystem().setHelp(this,
                   ContextIds.WEB_BROWSER);
         }

         // create a new SWT Web browser widget, checking once again to make sure
 // we can use it in this environment
 //if (WebBrowserUtil.canUseInternalWebBrowser())
 try {
             this.browser = new Browser(this, SWT.NONE);
         }
         catch (SWTError e) {
             if (e.code!=SWT.ERROR_NO_HANDLES) {
                 WebBrowserUtil.openError(Messages.errorCouldNotLaunchInternalWebBrowser);
                 return;
             }
             text = new BrowserText(this, this, e);
         }

         if (showURLbar)
             updateHistory();
         if (showToolbar)
             updateBackNextBusy();

          if (browser!=null) {
             browser.setLayoutData(new GridData(GridData.FILL_BOTH));
             PlatformUI.getWorkbench().getHelpSystem().setHelp(browser,
                     ContextIds.WEB_BROWSER);
         }
         else
             text.getControl().setLayoutData(new GridData(GridData.FILL_BOTH));

         addBrowserListeners();
         //listen();
 }

     /**
      * Returns the underlying SWT browser widget.
      *
      * @return the underlying browser
      */
     public Browser getBrowser() {
         return browser;
     }

     /**
      * Navigate to the home URL.
      */
     public void home() {
      browser.setText(""); //$NON-NLS-1$
 }

     /**
      * Loads a URL.
      *
      * @param url
      * the URL to be loaded
      * @return true if the operation was successful and false otherwise.
      * @exception IllegalArgumentException
      * <ul>
      * <li>ERROR_NULL_ARGUMENT - if the url is null</li>
      * </ul>
      * @exception SWTException
      * <ul>
      * <li>ERROR_THREAD_INVALID_ACCESS when called from the
      * wrong thread</li>
      * <li>ERROR_WIDGET_DISPOSED when the widget has been
      * disposed</li>
      * </ul>
      * @see #getURL()
      */
     public void setURL(String url) {
        setURL(url, true);
     }

     protected void updateBackNextBusy() {
         back.setEnabled(isBackEnabled());
         forward.setEnabled(isForwardEnabled());
         busy.setBusy(loading);

         if (backNextListener != null)
             backNextListener.updateBackNextBusy();
     }

     protected void updateLocation() {
         if (locationListener != null)
             locationListener.historyChanged(null);

         if (locationListener != null)
             locationListener.locationChanged(null);
     }

     /**
      *
      */
     private void addBrowserListeners() {
         if (browser==null) return;
         // respond to ExternalBrowserInstance StatusTextEvents events by
 // updating the status line
 browser.addStatusTextListener(new StatusTextListener() {
             public void changed(StatusTextEvent event) {
                     //System.out.println("status: " + event.text); //$NON-NLS-1$
 if (container != null) {
                     IStatusLineManager status = container.getActionBars()
                             .getStatusLineManager();
                     status.setMessage(event.text);
                 }
             }
         });

         // Add listener for new window creation so that we can instead of
 // opening a separate
 // new window in which the session is lost, we can instead open a new
 // window in a new
 // shell within the browser area thereby maintaining the session.
 browser.addOpenWindowListener(new OpenWindowListener() {
             public void open(WindowEvent event) {
                 Shell shell2 = new Shell(getShell());
                 shell2.setLayout(new FillLayout());
                 shell2.setText(Messages.viewWebBrowserTitle);
                 shell2.setImage(getShell().getImage());
                 if (event.location != null)
                     shell2.setLocation(event.location);
                 if (event.size != null)
                     shell2.setSize(event.size);
                      int style = 0;
                      if (event.addressBar)
                          style += LOCATION_BAR;
                      if (event.toolBar)
                          style += BUTTON_BAR;
                 BrowserViewer browser2 = new BrowserViewer(shell2, 0);
                 browser2.newWindow = true;
                 event.browser = browser2.browser;
             }
         });
           
           browser.addVisibilityWindowListener(new VisibilityWindowListener() {
                 public void hide(WindowEvent e) {
                     // ignore
 }
                 
                 public void show(WindowEvent e) {
                     Browser browser2 = (Browser)e.widget;
                     if (browser2.getParent().getParent() instanceof Shell) {
                         Shell shell = (Shell) browser2.getParent().getParent();
                         if (e.location != null)
                             shell.setLocation(e.location);
                         if (e.size != null)
                             shell.setSize(shell.computeSize(e.size.x, e.size.y));
                         shell.open();
                     }
                 }
             });

         browser.addCloseWindowListener(new CloseWindowListener() {
             public void close(WindowEvent event) {
                 // if shell is not null, it must be a secondary popup window,
 // else its an editor window
 if (newWindow)
                     getShell().dispose();
                 else
                     container.close();
             }
         });

         browser.addProgressListener(new ProgressListener() {
             public void changed(ProgressEvent event) {
                     //System.out.println("progress: " + event.current + ", " + event.total); //$NON-NLS-1$ //$NON-NLS-2$
 if (event.total == 0)
                     return;

                 boolean done = (event.current == event.total);

                 int percentProgress = event.current * 100 / event.total;
                 if (container != null) {
                     IProgressMonitor monitor = container.getActionBars()
                             .getStatusLineManager().getProgressMonitor();
                     if (done) {
                         monitor.done();
                         progressWorked = 0;
                     } else if (progressWorked == 0) {
                         monitor.beginTask("", event.total); //$NON-NLS-1$
 progressWorked = percentProgress;
                     } else {
                         monitor.worked(event.current - progressWorked);
                         progressWorked = event.current;
                     }
                 }

                 if (showToolbar) {
                     if (!busy.isBusy() && !done)
                         loading = true;
                     else if (busy.isBusy() && done) // once the progress hits
 // 100 percent, done, set
 // busy to false
 loading = false;

                           //System.out.println("loading: " + loading); //$NON-NLS-1$
 updateBackNextBusy();
                     updateHistory();
                 }
             }

             public void completed(ProgressEvent event) {
                 if (container != null) {
                     IProgressMonitor monitor = container.getActionBars()
                             .getStatusLineManager().getProgressMonitor();
                     monitor.done();
                 }
                 if (showToolbar) {
                     loading = false;
                     updateBackNextBusy();
                     updateHistory();
                 }
             }
         });

         if (showToolbar) {
             browser.addLocationListener(new LocationListener() {
                 public void changed(LocationEvent event) {
                     if (!event.top)
                         return;
                     if (combo != null) {
                         if (!"about:blank".equals(event.location)) { //$NON-NLS-1$
 combo.setText(event.location);
                             addToHistory(event.location);
                             updateHistory();
                         }// else
 // combo.setText(""); //$NON-NLS-1$
 }
                 }

                 public void changing(LocationEvent event) {
                     // do nothing
 }
             });
         }

         browser.addTitleListener(new TitleListener() {
             public void changed(TitleEvent event) {
                      String oldTitle = title;
                 title = event.title;
                      firePropertyChangeEvent(PROPERTY_TITLE, oldTitle, title);
             }
         });
     }
      
      /**
          * Add a property change listener to this instance.
          *
          * @param listener java.beans.PropertyChangeListener
          */
         public void addPropertyChangeListener(PropertyChangeListener listener) {
             if (propertyListeners == null)
                 propertyListeners = new ArrayList ();
             propertyListeners.add(listener);
         }

         /**
          * Remove a property change listener from this instance.
          *
          * @param listener java.beans.PropertyChangeListener
          */
         public void removePropertyChangeListener(PropertyChangeListener listener) {
             if (propertyListeners != null)
                 propertyListeners.remove(listener);
         }

         /**
          * Fire a property change event.
          */
         protected void firePropertyChangeEvent(String propertyName, Object oldValue, Object newValue) {
             if (propertyListeners == null)
                 return;

             PropertyChangeEvent event = new PropertyChangeEvent (this, propertyName, oldValue, newValue);
             //Trace.trace("Firing: " + event + " " + oldValue);
 try {
                 int size = propertyListeners.size();
                 PropertyChangeListener [] pcl = new PropertyChangeListener [size];
                 propertyListeners.toArray(pcl);
                 
                 for (int i = 0; i < size; i++)
                     try {
                         pcl[i].propertyChange(event);
                     } catch (Exception e) {
                         // ignore
 }
             } catch (Exception e) {
                 // ignore
 }
         }

     /**
      * Navigate to the next session history item. Convenience method that calls
      * the underlying SWT browser.
      *
      * @return <code>true</code> if the operation was successful and
      * <code>false</code> otherwise
      * @exception SWTException
      * <ul>
      * <li>ERROR_THREAD_INVALID_ACCESS when called from the
      * wrong thread</li>
      * <li>ERROR_WIDGET_DISPOSED when the widget has been
      * disposed</li>
      * </ul>
      * @see #back
      */
     public boolean forward() {
         if (browser==null)
             return false;
         return browser.forward();
     }

     /**
      * Navigate to the previous session history item. Convenience method that
      * calls the underlying SWT browser.
      *
      * @return <code>true</code> if the operation was successful and
      * <code>false</code> otherwise
      * @exception SWTException
      * <ul>
      * <li>ERROR_THREAD_INVALID_ACCESS when called from the
      * wrong thread</li>
      * <li>ERROR_WIDGET_DISPOSED when the widget has been
      * disposed</li>
      * </ul>
      * @see #forward
      */
     public boolean back() {
         if (browser==null)
             return false;
         return browser.back();
     }

     /**
      * Returns <code>true</code> if the receiver can navigate to the previous
      * session history item, and <code>false</code> otherwise. Convenience
      * method that calls the underlying SWT browser.
      *
      * @return the receiver's back command enabled state
      * @exception SWTException
      * <ul>
      * <li>ERROR_WIDGET_DISPOSED - if the receiver has been
      * disposed</li>
      * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
      * thread that created the receiver</li>
      * </ul>
      * @see #back
      */
     public boolean isBackEnabled() {
         if (browser==null)
             return false;
         return browser.isBackEnabled();
     }

     /**
      * Returns <code>true</code> if the receiver can navigate to the next
      * session history item, and <code>false</code> otherwise. Convenience
      * method that calls the underlying SWT browser.
      *
      * @return the receiver's forward command enabled state
      * @exception SWTException
      * <ul>
      * <li>ERROR_WIDGET_DISPOSED - if the receiver has been
      * disposed</li>
      * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
      * thread that created the receiver</li>
      * </ul>
      * @see #forward
      */
     public boolean isForwardEnabled() {
         if (browser==null)
             return false;
         return browser.isForwardEnabled();
     }

     /**
      * Stop any loading and rendering activity. Convenience method that calls
      * the underlying SWT browser.
      *
      * @exception SWTException
      * <ul>
      * <li>ERROR_THREAD_INVALID_ACCESS when called from the
      * wrong thread</li>
      * <li>ERROR_WIDGET_DISPOSED when the widget has been
      * disposed</li>
      * </ul>
      */
     public void stop() {
         if (browser!=null)
             browser.stop();
     }

     /**
      *
      */
     private boolean navigate(String url) {
         Trace.trace(Trace.FINER, "Navigate: " + url); //$NON-NLS-1$
 if (url != null && url.equals(getURL())) {
             refresh();
             return true;
         }
         if (browser!=null)
             return browser.setUrl(url);
         return text.setUrl(url);
     }
  
     /**
      * Refresh the current page. Convenience method that calls the underlying
      * SWT browser.
      *
      * @exception SWTException
      * <ul>
      * <li>ERROR_THREAD_INVALID_ACCESS when called from the
      * wrong thread</li>
      * <li>ERROR_WIDGET_DISPOSED when the widget has been
      * disposed</li>
      * </ul>
      */
     public void refresh() {
         if (browser!=null)
             browser.refresh();
         else
             text.refresh();
           try {
               Thread.sleep(50);
           } catch (Exception e) {
               // ignore
 }
     }

     private void setURL(String url, boolean browse) {
         Trace.trace(Trace.FINEST, "setURL: " + url + " " + browse); //$NON-NLS-1$ //$NON-NLS-2$
 if (url == null) {
             home();
             return;
         }

         if ("eclipse".equalsIgnoreCase(url)) //$NON-NLS-1$
 url = "http://www.eclipse.org"; //$NON-NLS-1$
 else if ("wtp".equalsIgnoreCase(url)) //$NON-NLS-1$
 url = "http://www.eclipse.org/webtools/"; //$NON-NLS-1$

         if (browse)
             navigate(url);

         addToHistory(url);
         updateHistory();
     }

     protected void addToHistory(String url) {
         if (history == null)
             history = WebBrowserPreference.getInternalWebBrowserHistory();
         int found = -1;
         int size = history.size();
         for (int i = 0; i < size; i++) {
             String s = (String ) history.get(i);
             if (s.equals(url)) {
                 found = i;
                 break;
             }
         }

         if (found == -1) {
             if (size >= MAX_HISTORY)
                 history.remove(size - 1);
             history.add(0, url);
             WebBrowserPreference.setInternalWebBrowserHistory(history);
         } else if (found != 0) {
             history.remove(found);
             history.add(0, url);
             WebBrowserPreference.setInternalWebBrowserHistory(history);
         }
     }

     /**
      *
      */
     public void dispose() {
         super.dispose();

         showToolbar = false;

         if (busy != null)
             busy.dispose();
         busy = null;

         browser = null;
         text = null;
         if (clipboard!=null)
             clipboard.dispose();
         clipboard=null;

         removeSynchronizationListener();
     }

     private ToolBar createLocationBar(Composite parent) {
         combo = new Combo(parent, SWT.DROP_DOWN);

         updateHistory();

         combo.addSelectionListener(new SelectionAdapter() {
             public void widgetSelected(SelectionEvent we) {
                 try {
                     if (combo.getSelectionIndex() != -1)
                         setURL(combo.getItem(combo.getSelectionIndex()));
                 } catch (Exception e) {
                     // ignore
 }
             }
         });
         combo.addListener(SWT.DefaultSelection, new Listener () {
             public void handleEvent(Event e) {
                 setURL(combo.getText());
             }
         });
         
         ToolBar toolbar = new ToolBar(parent, SWT.FLAT);

         ToolItem go = new ToolItem(toolbar, SWT.NONE);
         go.setImage(ImageResource.getImage(ImageResource.IMG_ELCL_NAV_GO));
         go.setHotImage(ImageResource.getImage(ImageResource.IMG_CLCL_NAV_GO));
         go.setDisabledImage(ImageResource
                 .getImage(ImageResource.IMG_DLCL_NAV_GO));
         go.setToolTipText(Messages.actionWebBrowserGo);
         go.addSelectionListener(new SelectionAdapter() {
             public void widgetSelected(SelectionEvent event) {
                 setURL(combo.getText());
             }
         });
           
           return toolbar;
     }

     private ToolBar createToolbar(Composite parent) {
           ToolBar toolbar = new ToolBar(parent, SWT.FLAT);
           
         // create back and forward actions
 back = new ToolItem(toolbar, SWT.NONE);
         back.setImage(ImageResource
                 .getImage(ImageResource.IMG_ELCL_NAV_BACKWARD));
         back.setHotImage(ImageResource
                 .getImage(ImageResource.IMG_CLCL_NAV_BACKWARD));
         back.setDisabledImage(ImageResource
                 .getImage(ImageResource.IMG_DLCL_NAV_BACKWARD));
         back.setToolTipText(Messages.actionWebBrowserBack);
         back.addSelectionListener(new SelectionAdapter() {
             public void widgetSelected(SelectionEvent event) {
                 back();
             }
         });

         forward = new ToolItem(toolbar, SWT.NONE);
         forward.setImage(ImageResource
                 .getImage(ImageResource.IMG_ELCL_NAV_FORWARD));
         forward.setHotImage(ImageResource
                 .getImage(ImageResource.IMG_CLCL_NAV_FORWARD));
         forward.setDisabledImage(ImageResource
                 .getImage(ImageResource.IMG_DLCL_NAV_FORWARD));
         forward.setToolTipText(Messages.actionWebBrowserForward);
         forward.addSelectionListener(new SelectionAdapter() {
             public void widgetSelected(SelectionEvent event) {
                 forward();
             }
         });

         // create refresh, stop, and print actions
 ToolItem stop = new ToolItem(toolbar, SWT.NONE);
         stop.setImage(ImageResource.getImage(ImageResource.IMG_ELCL_NAV_STOP));
         stop.setHotImage(ImageResource
                 .getImage(ImageResource.IMG_CLCL_NAV_STOP));
         stop.setDisabledImage(ImageResource
                 .getImage(ImageResource.IMG_DLCL_NAV_STOP));
         stop.setToolTipText(Messages.actionWebBrowserStop);
         stop.addSelectionListener(new SelectionAdapter() {
             public void widgetSelected(SelectionEvent event) {
                 stop();
             }
         });

         ToolItem refresh = new ToolItem(toolbar, SWT.NONE);
         refresh.setImage(ImageResource
                 .getImage(ImageResource.IMG_ELCL_NAV_REFRESH));
         refresh.setHotImage(ImageResource
                 .getImage(ImageResource.IMG_CLCL_NAV_REFRESH));
         refresh.setDisabledImage(ImageResource
                 .getImage(ImageResource.IMG_DLCL_NAV_REFRESH));
         refresh.setToolTipText(Messages.actionWebBrowserRefresh);
         refresh.addSelectionListener(new SelectionAdapter() {
             public void widgetSelected(SelectionEvent event) {
                 refresh();
             }
         });
           
           return toolbar;
     }

     /**
      * Returns the current URL. Convenience method that calls the underlying SWT
      * browser.
      *
      * @return the current URL or an empty <code>String</code> if there is no
      * current URL
      * @exception SWTException
      * <ul>
      * <li>ERROR_THREAD_INVALID_ACCESS when called from the
      * wrong thread</li>
      * <li>ERROR_WIDGET_DISPOSED when the widget has been
      * disposed</li>
      * </ul>
      * @see #setURL(String)
      */
     public String getURL() {
         if (browser!=null)
             return browser.getUrl();
         return text.getUrl();
     }

     public boolean setFocus() {
         if (browser!=null) {
             browser.setFocus();
             updateHistory();
             return true;
         }
         return super.setFocus();
     }

     /**
      * Update the history list to the global/shared copy.
      */
     protected void updateHistory() {
         if (combo == null)
             return;

         String temp = combo.getText();
         if (history == null)
             history = WebBrowserPreference.getInternalWebBrowserHistory();

         String [] historyList = new String [history.size()];
         history.toArray(historyList);
         combo.setItems(historyList);

         combo.setText(temp);
     }

     public IBrowserViewerContainer getContainer() {
         return container;
     }

     public void setContainer(IBrowserViewerContainer container) {
         if (container==null && this.container!=null) {
             IStatusLineManager manager = this.container.getActionBars().getStatusLineManager();
             if (manager!=null)
                 manager.getProgressMonitor().done();
         }
         this.container = container;
     }

     protected File file;
     protected long timestamp;
     protected Thread fileListenerThread;
     protected LocationListener locationListener2;
     protected Object syncObject = new Object ();
     
     protected void addSynchronizationListener() {
      if (fileListenerThread != null)
          return;
      
      fileListenerThread = new Thread ("Browser file synchronization") { //$NON-NLS-1$
 public void run() {
              while (fileListenerThread != null) {
                  try {
                      Thread.sleep(2000);
                  } catch (Exception e) {
                      // ignore
 }
                  synchronized (syncObject) {
                          if (file != null && file.lastModified() != timestamp) {
                          timestamp = file.lastModified();
                          Display.getDefault().syncExec(new Runnable () {
                                 public void run() {
                                     refresh();
                                 }
                          });
                          }
                       }
              }
          }
      };
      fileListenerThread.setDaemon(true);
      fileListenerThread.setPriority(Thread.MIN_PRIORITY);
      
      locationListener2 = new LocationListener() {
           public void changed(LocationEvent event) {
              File temp = getFile(event.location);
              if (temp != null && temp.exists()) {
                  synchronized (syncObject) {
                      file = temp;
                      timestamp = file.lastModified();
                      }
              } else
                  file = null;
           }
           
           public void changing(LocationEvent event) {
              // do nothing
 }
        };
        browser.addLocationListener(locationListener2);
        
        File temp = getFile(browser.getUrl());
      if (temp != null && temp.exists()) {
         file = temp;
         timestamp = file.lastModified();
      }
      fileListenerThread.start();
     }

     protected static File getFile(String location) {
      if (location == null)
          return null;
      if (location.startsWith("file:/")) //$NON-NLS-1$
 location = location.substring(6);
      
      return new File (location);
     }

     protected void removeSynchronizationListener() {
      if (fileListenerThread == null)
          return;
      
      fileListenerThread = null;
      browser.removeLocationListener(locationListener2);
      locationListener2 = null;
     }
 }

